#include "siftVocabulary.h"
#include "grabcut.h"
#include "utilitiesFunction.h"
#include "configurationParameters.h"

using namespace std;
using namespace cv;

siftVocabulary::siftVocabulary(int _size_dictionary, int _size_sample){
	this->size_dictionary = _size_dictionary;
	this->size_sample = _size_sample;
}

void siftVocabulary::getSampleOnImage(char *filename, int number_sample_on_image){
	IplImage *image = cvLoadImage(filename);
	Mat data_img = cvCloneImage(image);
	Grabcut grabCut;
	cout << "Begining GC" << endl;
	grabCut.segment(data_img, configurationParameters::gcRatioCenter());
	Mat gc_mask = grabCut.loadMask();
	cout << "Finish GC" << endl;
	
	// Dense Keypoint
	float initFeatureScale=1.f; 
	int featureScaleLevels=1;
    float featureScaleMul=0.1f;
    int initXyStep=6;
	int initImgBound=0;
    bool varyXyStepWithScale=true;
    bool varyImgBoundWithScale=false;
	
	DenseFeatureDetector detector(initFeatureScale,featureScaleLevels,featureScaleMul,initXyStep,initImgBound,varyXyStepWithScale,varyImgBoundWithScale);
	vector<KeyPoint> denseQueryKeypoints;
	detector.detect(data_img, denseQueryKeypoints,gc_mask);

	cout << "Dense Keypoint" << endl;

	// Create random sample index keypoints on image
	int max = denseQueryKeypoints.size() - 1;

	cout << "denseQueryKeypoints.size(): "<< denseQueryKeypoints.size() << endl;
	vector<int> index_random; 

	if(number_sample_on_image > denseQueryKeypoints.size()){
		number_sample_on_image = denseQueryKeypoints.size();
		for(int i = 0; i <denseQueryKeypoints.size() ; i++){
			index_random.push_back(i);
		}
	}
	else{
		int random;
		int uniqueflag;
		int i, j;

		for(i = 0; i < number_sample_on_image; i++) {
			 do {
				/* Assume things are unique... we'll reset this flag if not. */
				uniqueflag = 1;
				random = rand() % max+ 1;
				/* This loop checks for uniqueness */
				for (j = 0; j < i && uniqueflag == 1; j++) {
					if (index_random.at(j) == random) {
					  uniqueflag = 0;
				   }
				}
			 } while (uniqueflag != 1);
			 index_random.push_back(random);
		}
	}

	cout << "Create random sample index keypoints on image" << endl;
	cout << "index_random.size(): "<< index_random.size() << endl;
	cout << "number_sample_on_image: "<< number_sample_on_image << endl;

	// Get sample keypoints
	vector<KeyPoint> sample_keypoint_on_image;

	for(int i = 0; i < number_sample_on_image; i++){
		int index_keypoint = index_random.at(i);
		try{
			KeyPoint kp = denseQueryKeypoints.at(index_keypoint);
		
			sample_keypoint_on_image.push_back(kp);
			this->sample_keypoint.push_back(kp);
		}
		catch(const std::exception& ex){
			cout << "Error at: " << index_keypoint << endl;
			int stop;
			cin >> stop;
		}
		
	}

	cout << "Get sample keypoints" << endl;

	// Get sample sift descriptor on image
	SiftDescriptorExtractor extractor;
    Mat siftDescriptors;
	extractor.compute(data_img, sample_keypoint_on_image, siftDescriptors);

	cout << "Get sample sift descriptor on image" << endl;

	// Push to sample_descriptor
	this->sample_sift_descriptor.push_back(siftDescriptors);

	cout << sample_sift_descriptor.size() << endl;
	cout << endl;
	cout << "====================" << endl;
}

void siftVocabulary::getSampleOnTrainingSet(string directory, string file_name_format, int from_file, int to_file){
	int number_files = (to_file - from_file)+1;
	int number_sample_on_image = this->size_sample / number_files;
	//int number_sample_on_image = 100;
	int re_assign_number_sample = number_sample_on_image * number_files;
	this->size_sample = re_assign_number_sample;

	// Call getSampleOnImage
	for(int i = from_file; i <= to_file  ; i++){
		string order_file = "";
		if(i < 10){
			order_file="000"+std::to_string(i);
			
		}
		if(10 <= i & i < 100){
			order_file="00"+std::to_string(i);
			
		}
		if(100 <= i & i < 1000){
			order_file="0"+std::to_string(i);
		}
		if(1000 <= i & i < 10000){
			order_file= std::to_string(i);
		}
		
		string string_path_file_image = directory + file_name_format+"_"+ order_file+".jpg";

		cout << string_path_file_image << endl;
		cout << "====================" << endl;
		cout << endl;
		char *cstr_image = new char[string_path_file_image.length() + 1];
		strcpy(cstr_image, string_path_file_image.c_str());
		// do stuff
		getSampleOnImage(cstr_image, number_sample_on_image);
		delete [] cstr_image;
	}
	this->size_sample = this->sample_sift_descriptor.rows;
	cout << "Real sample size: " << this->sample_sift_descriptor.size() << endl;
	cout << "Rows size: " << this->sample_sift_descriptor.rows << endl;
	cout << "Cols size: " << this->sample_sift_descriptor.cols << endl;
	cout << "Finish to get sample!" << endl;
}

void siftVocabulary::generateDictionary(){
	cv::TermCriteria tc(CV_TERMCRIT_ITER,100,0.001);
	int retries = 1; 
	int flags = KMEANS_PP_CENTERS;
	cv::BOWKMeansTrainer bowTrainer(this->size_dictionary,tc,retries,flags);

	cout << "Real sample size: " << this->sample_sift_descriptor.size() << endl;
	cout << "Rows size: " << this->sample_sift_descriptor.rows << endl;
	cout << "Cols size: " << this->sample_sift_descriptor.cols << endl;

	cout << "Begining cluster!" << endl;
	this->dictionary = bowTrainer.cluster(this->sample_sift_descriptor);
	cout << "Finishing cluster!" << endl;
}

int siftVocabulary::getSizeDictionary(){
	return this->size_dictionary;
}

Mat siftVocabulary::getDictionary(){
	return this->dictionary;
}